﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="InvokeMethodBinderTests.cs" company="JDML Consulting Ltd.">
//   Copyright 2011 Photon Project - http://photon.codeplex.com
// </copyright>
// <summary>
//   Test for InvokeMethodBinder
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Photon.Windows.Input
{
    using System;
    using System.Reflection;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// Test for InvokeMethodBinder
    /// </summary>
    [TestClass]
    public class InvokeMethodBinderTests
    {
        /// <summary>
        /// Gets or sets the test context which provides information about and functionality
        /// for the current test run.
        /// </summary>
        public TestContext TestContext { get; set; }

        [TestMethod]
        public void When_binding_should_map_common_type_aliases()
        {
            var model = new AliasTestModel();
            RunAliasTest(model, "bool", default(bool));
            RunAliasTest(model, "sbyte", default(sbyte));
            RunAliasTest(model, "byte", default(byte));
            RunAliasTest(model, "short", default(short));
            RunAliasTest(model, "ushort", default(ushort));
            RunAliasTest(model, "int", default(int));
            RunAliasTest(model, "uint", default(uint));
            RunAliasTest(model, "long", default(long));
            RunAliasTest(model, "ulong", default(ulong));
            RunAliasTest(model, "string", default(string));
            RunAliasTest(model, "object", default(object));
        }

        private static void RunAliasTest<T>(AliasTestModel model, string typeAlias, T argument)
        {
            var binding = new InvokeMethodBinder().Bind(typeof(AliasTestModel), "Invoke(" + typeAlias.ToLower() + ')');
            binding.Execute(model, new object[] {argument});

            // verify we bound to the correct method
            Assert.AreEqual(typeof(T), model.LastInvokedMethod.GetParameters()[0].ParameterType);
        }

        /// <summary>
        /// Test to cover defect where enum and int arguments caused a ambigous match exception.
        /// </summary>
        [TestMethod]
        public void When_binding_should_distinguish_between_enum_arguments_and_int32()
        {
            var binding1 = new InvokeMethodBinder().Bind(typeof(EnumTestModel), "Invoke(AttributeTargets)");
            Assert.AreEqual(binding1.ExecuteMethod, typeof(EnumTestModel).GetMethod("Invoke", new[] { typeof(AttributeTargets) }));

            var binding2 = new InvokeMethodBinder().Bind(typeof(EnumTestModel), "Invoke(int)");
            Assert.AreEqual(binding2.ExecuteMethod, typeof(EnumTestModel).GetMethod("Invoke", new[] { typeof(int) }));
        }

        [TestMethod]
        public void When_binding_should_allow_can_execute_property_to_be_shared()
        {
            // get can execute property
            var canExecuteProperty = typeof(SharedCanExecuteTestModel).GetProperty("CanInvoke");
            Assert.IsNotNull(canExecuteProperty);

            // validate it is shared between Invoke() and Invoke(int)
            var binding1 = new InvokeMethodBinder().Bind(typeof(SharedCanExecuteTestModel), "Invoke()");
            Assert.AreEqual(binding1.ExecuteMethod, typeof(SharedCanExecuteTestModel).GetMethod("Invoke", new Type[0]));
            Assert.AreEqual(binding1.CanExecuteMethod, canExecuteProperty.GetGetMethod());
            var binding2 = new InvokeMethodBinder().Bind(typeof(SharedCanExecuteTestModel), "Invoke(int)");
            Assert.AreEqual(binding2.ExecuteMethod, typeof(SharedCanExecuteTestModel).GetMethod("Invoke", new[] { typeof(int) }));
            Assert.AreEqual(binding2.CanExecuteMethod, canExecuteProperty.GetGetMethod());
        }

        [TestMethod]
        public void When_binding_should_distinguish_between_types_that_have_the_same_name_by_namespace()
        {
            var binding1 = new InvokeMethodBinder().Bind(typeof(DuplicateTypeNameTestModel), "Invoke(AmbigousMatch1.TestObject)");
            Assert.AreEqual(binding1.ExecuteMethod,
                typeof(DuplicateTypeNameTestModel).GetMethod("Invoke", new[] { typeof(AmbigousMatch1.TestObject) }));

            var binding2 = new InvokeMethodBinder().Bind(typeof(DuplicateTypeNameTestModel), "Invoke(AmbigousMatch2.TestObject)");
            Assert.AreEqual(binding2.ExecuteMethod,
                typeof(DuplicateTypeNameTestModel).GetMethod("Invoke", new[] { typeof(AmbigousMatch2.TestObject) }));
        }

        #region Nested type: AliasTestModel

        public class AliasTestModel
        {
            public MethodBase LastInvokedMethod;

            public void Invoke(byte argument)
            {
                LastInvokedMethod = MethodBase.GetCurrentMethod();
            }

            public void Invoke(sbyte argument)
            {
                LastInvokedMethod = MethodBase.GetCurrentMethod();
            }

            public void Invoke(short argument)
            {
                LastInvokedMethod = MethodBase.GetCurrentMethod();
            }

            public void Invoke(ushort argument)
            {
                LastInvokedMethod = MethodBase.GetCurrentMethod();
            }

            public void Invoke(int argument)
            {
                LastInvokedMethod = MethodBase.GetCurrentMethod();
            }

            public void Invoke(uint argument)
            {
                LastInvokedMethod = MethodBase.GetCurrentMethod();
            }

            public void Invoke(long argument)
            {
                LastInvokedMethod = MethodBase.GetCurrentMethod();
            }

            public void Invoke(ulong argument)
            {
                LastInvokedMethod = MethodBase.GetCurrentMethod();
            }

            public void Invoke(bool argument)
            {
                LastInvokedMethod = MethodBase.GetCurrentMethod();
            }

            public void Invoke(string argument)
            {
                LastInvokedMethod = MethodBase.GetCurrentMethod();
            }

            public void Invoke(object argument)
            {
                LastInvokedMethod = MethodBase.GetCurrentMethod();
            }
        }

        #endregion

        #region Nested type: DuplicateTypeNameTestModel

        public class DuplicateTypeNameTestModel
        {
            public void Invoke(AmbigousMatch1.TestObject argument)
            {
            }

            public void Invoke(AmbigousMatch2.TestObject argument)
            {
            }
        }

        #endregion

        #region Nested type: EnumTestModel

        public class EnumTestModel
        {
            public void Invoke(AttributeTargets argument)
            {
            }

            public void Invoke(int argument)
            {
            }
        }

        #endregion

        #region Nested type: SharedCanExecuteTestModel

        public class SharedCanExecuteTestModel
        {
            public bool CanInvoke
            {
                get { return true; }
            }

            public void Invoke(int argument)
            {
            }

            public void Invoke()
            {
            }
        }

        #endregion
    }

    namespace AmbigousMatch1
    {
        public class TestObject
        {
        }
    }

    namespace AmbigousMatch2
    {
        public class TestObject
        {
        }
    }
}